package com.sw.funfuck.common.util;

import lombok.Data;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * OKHttpUtil
 *
 * @author Johnathan
 **/
public final class OKHttpUtil {

    private static Logger logger = LoggerFactory.getLogger(OKHttpUtil.class);

    private static MediaType IMAGE_MEDIATYPE = MediaType.parse("image/jpg");
    private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");

    private static final OkHttpClient client = new OkHttpClient().newBuilder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(10, TimeUnit.SECONDS)
            .build();

    private OKHttpUtil(){

    }

    /**
     * 同步get请求方法
     * @param uri
     * @param params
     * @return
     */
    public static Response get(String uri, Map<String, String> params){
        String requestUri = buildGETParams(uri, params);
        OkHttpClient okHttpClient = buildClient();
        Request request = new Request.Builder()
                .url(requestUri)
                .build();
        try {
            Call call = okHttpClient.newCall(request);
            Response response = call.execute();
            return response;
        } catch (IOException e) {
            logger.error("[HTTP GET Request]url:{}, exception message:{}", uri, e.getCause());
        }
        return null;
    }

    /**
     * 同步post请求方法
     * @param uri
     * @param params
     * @return
     */
    public static Response post(String uri, Map<String, Object> params){
        OkHttpClient okHttpClient = buildClient();
        Request request = new Request.Builder()
                .url(uri)
                .post(buildPOSTBody(params))
                .build();
        try {
            Call call = okHttpClient.newCall(request);
            Response response = call.execute();
            return response;
        } catch (IOException e) {
            logger.error("[HTTP POST Request]url:{}, exception message:{}", uri, e.getMessage());
        }
        return null;
    }

    /**
     * 同步post请求方法
     * @param uri
     * @param params
     * @return
     */
    public static String requestPost(String uri, Map<String, Object> params) throws IOException {
        OkHttpClient okHttpClient = buildClient();
        Request request = new Request.Builder()
                .url(uri)
                .post(buildPOSTBody(params))
                .build();
        ResponseBody responseBody = null;
        String responseStr = null;
        try {
            Call call = okHttpClient.newCall(request);
            Response response = call.execute();
            responseBody = response.body();
            if(response.isSuccessful()){
                responseStr = responseBody.string();
            }else {
                logger.error("[OKHttpUtil] request {} error.Code: {},Cause: {}.", response.code(), responseBody.string());
            }
        }finally {
            if (null != responseBody){
                responseBody.close();
            }
        }
        return responseStr;
    }

    /**
     * 同步post请求方法
     * @param uri
     * @param json
     * @return
     */
    public static Response postJSONStr(String uri, String json) throws IOException {
        OkHttpClient okHttpClient = buildClient();
        RequestBody requestBody = RequestBody.create(JSON, json);
        Request request = new Request.Builder()
                .url(uri)
                .post(requestBody)
                .build();

        Call call = okHttpClient.newCall(request);
        return call.execute();
    }

    /**
     * 设置GET请求参数
     * @param uri
     * @param params
     * @return
     */
    private static String buildGETParams(String uri, Map<String, String> params){
        StringBuilder paramsBuilder = new StringBuilder(uri);
        paramsBuilder.append("?");
        if(!CollectionUtils.isEmpty(params)){
            params.forEach((key,value) -> {
                paramsBuilder.append(key);
                paramsBuilder.append("=");
                paramsBuilder.append(value);
                paramsBuilder.append("&");
            });
        }
        return paramsBuilder.substring(0, paramsBuilder.length()-1).toString();
    }

    /**
     * 设置POST请求参数
     * @param params
     * @return
     */
    private static RequestBody buildPOSTBody(Map<String, Object> params){
        RequestBody requestBody = null;
        if (!CollectionUtils.isEmpty(params)){
            if(isMultipartRequest(params)){
                requestBody = buildMultipartBody(params);
            }else{
                requestBody = buildRequestBody(params);
            }
        } else {
            requestBody = new FormBody.Builder().build();
        }
        return requestBody;
    }

    /**
     * 设置POST文件参数
     * @param params
     * @return
     */
    private static MultipartBody buildMultipartBody(Map<String, Object> params){
        MultipartBody.Builder multipartBodyBuild = new MultipartBody.Builder();
        multipartBodyBuild.setType(MultipartBody.FORM);

        params.forEach((key, value) -> {
            //集合参数
            if (value instanceof Collection){
                if(!CollectionUtils.isEmpty((Collection) value)){
                    ((Collection) value).stream().forEach(item -> {
                        setMultiPart(multipartBodyBuild, key, item);
                    });
                }
            } else{
                setMultiPart(multipartBodyBuild, key, value);
            }

        });

        return multipartBodyBuild.build();
    }

    /**
     * 设置文件参数
     * @param multipartBodyBuild
     * @param key
     * @param value
     */
    private static void setMultiPart(MultipartBody.Builder multipartBodyBuild, String key, Object value){
        if (value instanceof File){
            File file = (File) value;
            RequestBody reqBody = RequestBody.create(IMAGE_MEDIATYPE, file);
            multipartBodyBuild.addFormDataPart(key, file.getName(), reqBody);
        } else if (value instanceof FileParamDTO) {
            FileParamDTO file = (FileParamDTO) value;
            RequestBody reqBody = RequestBody.create(IMAGE_MEDIATYPE, file.getBuff());
            multipartBodyBuild.addFormDataPart(key, file.getFileName(), reqBody);
        } else{
            multipartBodyBuild.addFormDataPart(key, String.valueOf(value));
        }
    }

    /**
     * 设置普通POST BODY参数
     * @param params
     * @return
     */
    private static RequestBody buildRequestBody(Map<String, Object> params){
        FormBody.Builder formBodyBuilder = new FormBody.Builder();

        params.forEach((key, value) -> {
            //集合参数
            if (value instanceof Collection){
                if(!CollectionUtils.isEmpty((Collection) value)){
                    ((Collection) value).stream().forEach(item -> {
                        formBodyBuilder.add(key, String.valueOf(item));
                    });
                }
            } else{
                formBodyBuilder.add(key, String.valueOf(value));
            }

        });

        return formBodyBuilder.build();
    }

    /**
     * 是否包含文件参数
     * @param params
     * @return
     */
    private static boolean isMultipartRequest(Map<String, Object> params){
        boolean isMultipart = false;

        for (String key : params.keySet()){
            if(params.get(key) instanceof File
                    || params.get(key) instanceof FileParamDTO){
                isMultipart = true;
                break;
            }

            if (params.get(key) instanceof Collection){
                if(((Collection) params.get(key)).stream().anyMatch(item -> {
                    return item instanceof File || item instanceof FileParamDTO;
                })){
                    isMultipart = true;
                    break;
                }
            }
        }

        return isMultipart;
    }

    private static OkHttpClient buildClient(){
        return client;
    }

    @Data
    public static class FileParamDTO {
        /**
         * the image-bytes
         */
        private byte[] buff;

        /**
         * filename
         */
        private String fileName;
    }
}
